package org.example.common.validation.enumcheck;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;
import java.util.LinkedHashMap;

/**
 * 枚举类型 约束注解类
 *
 * @author kenx
 * @version 1.0
 * @since 2021/1/21 20:49
 */
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = EnumCheck.Validator.class) // 标明由哪个类执行校验逻辑
public @interface EnumCheck {
    
    // 校验出错时默认返回的消息
    String message() default "String类的值必须是枚举类型中的任意一个";
    
    // 枚举类
    Class<? extends Enum<?>> enumClass();
    
    //分组校验
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
    
    //下面是自己定义属性
    String field() default ""; //检查 成员变量 字段名称, 为空的情况下检查枚举类名称
    
    boolean allowNull() default false; // 是否允许为空
    
    class Validator implements ConstraintValidator<EnumCheck, Object> {
        private Class<? extends Enum<?>> enumClass;
        private boolean allowNull;
        private String field;
        
        @Override
        public void initialize(EnumCheck enumValue) {
            enumClass = enumValue.enumClass();
            allowNull = enumValue.allowNull();
            field = enumValue.field();
        }
        
        @Override
        //需要保证线程安全，因为它可能是多线程调用。
        public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
            if (value == null) {
                return allowNull;
            }
            
            if (enumClass == null) {
                return Boolean.TRUE;
            }
            if (StrUtil.isNotBlank(field)) {
                return EnumUtil.getNameFieldMap(enumClass, field).containsValue(value.toString());
            }
            
            return getEnumMap(enumClass).containsKey(value.toString());
        }
        
        public static <E extends Enum<E>> LinkedHashMap<String, E> getEnumMap(final Class<? extends Enum<?>> enumClass) {
            final LinkedHashMap<String, E> map = new LinkedHashMap<>();
            for (final Enum<?> e : enumClass.getEnumConstants()) {
                map.put(e.name(), (E) e);
            }
            return map;
        }
    }
}